package com.hk.system.config;


import com.hk.domain.service.UserService;
import com.hk.security.authentication.TokenAuthenticationEntryPoint;
import com.hk.security.authentication.TokenAuthenticationProvider;
import com.hk.security.filter.TokenAuthenticationFilter;
import dubhe.token.api.authentication.DefaultTokenAuthentication;
import dubhe.token.api.factory.TokenFactory;
import dubhe.token.api.registry.TokenRegistry;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.Cookie;

/**
 * swagger配置类
 */
@Configuration
@Order(1)
public class SwaggerConfig {
    private final PasswordEncoder passwordEncoder;
    private final TokenRegistry tokenRegistry;
    private final TokenAuthenticationProvider tokenAuthenticationProvider;
    private final TokenFactory tokenFactory;
    private final UserService userService;

    public SwaggerConfig(PasswordEncoder passwordEncoder,
                         TokenRegistry tokenRegistry,
                         TokenAuthenticationProvider tokenAuthenticationProvider,
                         @Qualifier(TokenFactory.BEAN_NAME) TokenFactory tokenFactory,
                         UserService userService) {
        this.passwordEncoder = passwordEncoder;
        this.tokenRegistry = tokenRegistry;
        this.tokenAuthenticationProvider = tokenAuthenticationProvider;
        this.tokenFactory = tokenFactory;
        this.userService = userService;
    }

    @Bean
    public GroupedOpenApi systemApi() {
        return GroupedOpenApi.builder()
                .group("后台管理")
                .packagesToScan()
                .pathsToMatch("/rest/**")
                .build();
    }

    @Bean
    @Order(1)
    public SecurityFilterChain swaggerFilterChain(HttpSecurity http) throws Exception {
        http.requestMatchers()
                .antMatchers("/swagger-ui/**", "/v3/api-docs/**", "/login", "/login.html")
                .and()
                .authorizeRequests()
                .antMatchers("/login", "/login.html", "/v3/api-docs/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .authenticationProvider(daoAuthenticationProvider())
                .authenticationProvider(tokenAuthenticationProvider)
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .formLogin(configurer -> {
                    configurer.loginPage("/login.html");
                    configurer.loginProcessingUrl("/login");
                    configurer.successHandler((request, response, authentication) -> {
                        if (authentication instanceof DefaultTokenAuthentication) {
                            DefaultTokenAuthentication tokenAuthentication = (DefaultTokenAuthentication) authentication;
                            Cookie tokenCookie = new Cookie("ACCESS_TOKEN", tokenAuthentication.getToken());
                            tokenCookie.setPath("/");
                            tokenCookie.setMaxAge(3600);
                            response.addCookie(tokenCookie);
                            response.sendRedirect("swagger-ui/index.html");
                        }
                    });
                })
                .apply(AuthenticationFilterDsl.custom(new TokenAuthenticationEntryPoint()));
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl(userService);
    }

    public DatabaseAuthenticationProvider daoAuthenticationProvider() {
        final DatabaseAuthenticationProvider daoAuthenticationProvider = new DatabaseAuthenticationProvider(tokenFactory, tokenRegistry);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        return daoAuthenticationProvider;
    }


    public static class AuthenticationFilterDsl extends AbstractHttpConfigurer<AuthenticationFilterDsl, HttpSecurity> {

        private final TokenAuthenticationEntryPoint entryPoint;

        public AuthenticationFilterDsl(TokenAuthenticationEntryPoint entryPoint) {
            this.entryPoint = entryPoint;
        }

        @Override
        public void configure(HttpSecurity http) {
            AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
            http.addFilterBefore(new TokenAuthenticationFilter(authenticationManager, entryPoint), UsernamePasswordAuthenticationFilter.class);
        }

        public static AuthenticationFilterDsl custom(TokenAuthenticationEntryPoint entryPoint) {
            return new AuthenticationFilterDsl(entryPoint);
        }
    }
}
